Compressive Sensing

The task is to extract images or signals accurately and even exactly from a number of samples which is far smaller than the desired resolution of the image/signal, e.g., the number of pixels in the image. This new technique draws from results in several fields

Suppose we are given a sparse signal.

Can we recover the signal with small number of measurements (far smaller than the desired resolution of the signal)?

The answer is _YES, for some signals and carefully selected measurements using $l_1$ minimization._

Prerequisites

The reader should be familiar to elementary concepts about signals, with linear algebra concepts, and linear programming.

Competences

The reader should be able to recover a signal from a small number of measurements.

References

For more details see

Credits: Daniel Bragg, an IASTE Intern, performed testing of some of the methods.

Underdetermined systems

Let $A\in\mathbb{R}^{m\times n}$ with $m<n$, $x\in\mathbb{R}^n$ and $b\in\mathbb{R}^m$.

Definitions

The system $Ax=b$ is underdetermined.

$\|x\|_0$ is the number of nonzero entries of $x$ (a quasi-norm).

A matrix $A$ satisfies the restricted isometry property (RIP) of order $k$ with constant $\delta_k\in(0,1)$ if

$$ (1 − \delta_k )\|x\|_2^2 \leq \| Ax\|_2^2 \leq (1 + \delta_k)\| x\|_2^2 $$

for any $x$ such that $\|x\|_0 \leq k$.

A mutual incoherence of a matrix $A$ is

$$ \mathcal{M}(A)= \max_{i \neq j} |[A^TA]_{ij}|, $$

that is, the absolutely maximal inner product of distinct columns of $A$. If the columns of $A$ have unit norms, $\mathcal{M}(A)\in[0,1]$.

The spark of a given matrix $A$, $spark(A)$, is the smallest number of columns of $A$ that are linearly dependant.

Facts

  1. An underdetermined system either has no solution or has infinitely many solutions.

  2. The typical problem is to choose the solution of some minimal norm. This problem can be reformulated as a constrained optimization problem $$ \textrm{minimize}\ \|x\| \quad \textrm{subject to} \quad Ax=b. $$ In particular:

    1. For the 2-norm, the $l_2$ minimization problem is solved by SVD: let $\mathop{\mathrm{rank}}(A)=r$ and let $A=U\Sigma V^T$ be the SVD of $A$. Then $$ x=\sum_{k=1}^r \frac{U[:,k]^Tb}{\sigma_k} V[:,k]. $$
    2. For the 1-norm, the $l_1$ minimization problem is a linear programming problem $$\textrm{minimize}\ \ c^T x \quad \textrm{subject to} \quad Ax=b,\ x\geq 0,$$ for $c^T=\begin{bmatrix}1 & 1 & \cdots & 1 \end{bmatrix}$.
    3. For the 0-norm, the $l_0$ problem (which appears in compressive sensing) $$ \textrm{minimize}\ \|x\|_0 \quad \textrm{subject to} \quad Ax=b, $$ is NP-hard.
  3. It holds $spark(A)\in[2,m+1]$.
  4. For any vector $b$, there exists at most one vector $x$ such that $\|x\|_0\leq k$ and $Ax=b$ if and only if $spark(A) > 2k$. This implies that $m\geq 2k$, which is a good choice when we are computing solutions which are exactly sparse.

  5. If $k<\displaystyle \frac{1}{2} \left(1+\frac{1}{\mathcal{M}(A)}\right)$, then for any vector $b$ there exists at most one vector $x$ such that $\|x\|_0\leq k$ and $Ax=b$.

  6. If the solution $x$ of $l_0$ problem satisfies $\|x\|_0 < \displaystyle \frac{\sqrt{2}-1/2}{\mathcal{M}(A)}$, then the solution of $l_1$ problem is the solution of $l_0$ problem!

  7. If $A$ has columns of unit-norm, then $A$ satisfies the RIP of order $k$ with $\delta_k = (k − 1)\mathcal{M}(A)$ for all $k < 1/\mathcal{M}(A)$.

  8. If $A$ satisfies RIP of order $2k$ with $\delta_{2k}<\sqrt{2}-1$, then the solution of $l_1$ problem is the solution of $l_0$ problem!

  9. Checking whether the specific matrix has RIP is difficult. If $m ≥ C \cdot k \log\left(\displaystyle\frac{n}{k}\right)$, where $C$ is some constant depending on each instance, the following classes of matrices satisfy RIP with $\delta_{2k}<\sqrt{2}-1$ with overwhelming probability(the matrices are normalised to have columns with unit norms):

    1. Form $A$ by sampling at random $n$ column vectors on the unit sphere in $\mathbb{R}^m$.
    2. Form $A$ by sampling entries from the normal distribution with mean 0 and variance $1/ m$.
    3. Form $A$ by sampling entries from a symmetric Bernoulli distribution $P(A_{ij} = ±1/\sqrt{m}) = 1/2$.
    4. Form $A$ by sampling at random $m$ rows of the Fourier matrix.
  10. The compressive sensing interpretation is the following: the signal $x$ is reconstructed from samples with $m$ functionals (the rows of $A$).

Example - $l_2$ minimization


In [4]:
A=rand(5,8)
b=rand(5)
A


Out[4]:
5×8 Array{Float64,2}:
 0.464003  0.484634  0.102289  0.720833  …  0.021741   0.501337  0.803386
 0.956128  0.930072  0.353645  0.822314     0.892963   0.90272   0.171284
 0.819552  0.216793  0.100109  0.515139     0.146014   0.830219  0.660244
 0.996858  0.822389  0.781405  0.219264     0.0430696  0.912659  0.247236
 0.218095  0.79105   0.992193  0.561354     0.435231   0.893051  0.542027

In [5]:
b


Out[5]:
5-element Array{Float64,1}:
 0.2720136839453131 
 0.1933344515201203 
 0.6359654704438957 
 0.04233252758660222
 0.9005993075721412 

In [6]:
using LinearAlgebra
x=A\b
U,σ,V=svd(A)
norm(A*x-b), norm( sum( [(U[:,k]'*b/σ[k])[1]*V[:,k]  for k=1:5])-x)


Out[6]:
(1.566162308693318e-15, 2.686992031160284e-15)

Examples - Exact sparse signal recovery

We recover randomly generated sparse signals "measured" with rows of the matrix $A$. The experiment is performed for types of matrices from Fact 9.

The $l_1$ minimization problem is solved using the function linprog() from the package MathProgBase.jl. This function requires the linear programming solver from the package Clp.jl be installed beforehand (it is a longer compilation). MathProgBase.jl is now depraected in favour of MathOptInterface.jl, but we keep it for this lecture for the sake of simplicity. Good choice is also JuMP.jl.

Random matrices are generated using the package Distributions.jl.

For more details see the documentation.


In [7]:
# import Pkg; Pkg.add("Clp")
# import Pkg; Pkg.add("GLPKMathProgInterface")
# import Pkg; Pkg.add("MathProgBase")
# import Pkg; Pkg.add("Distributions")

In [11]:
using Plots
using Clp
using GLPKMathProgInterface
# using Gurobi
using MathProgBase
using Distributions

In [12]:
varinfo(MathProgBase)


Out[12]:
name size summary
MathProgBase 369.774 KiB Module
buildlp 0 bytes typeof(buildlp)
linprog 0 bytes typeof(linprog)
mixintprog 0 bytes typeof(mixintprog)
quadprog 0 bytes typeof(quadprog)
solvelp 0 bytes typeof(solvelp)

Small example

\begin{split}\min_{x,y}\, &-x\\ s.t.\quad &2x + y \leq 1.5\\ & x \geq 0, y \geq 0\end{split}

In [13]:
l1 = linprog([-1,0],[2 1],'<',1.5,ClpSolver())


Out[13]:
MathProgBase.HighLevelInterface.LinprogSolution(:Optimal, -0.75, [0.75, 0.0], Dict{Any,Any}(:lambda=>[-0.5],:redcost=>[0.0, 0.5]))

In [14]:
fieldnames(typeof(l1))


Out[14]:
(:status, :objval, :sol, :attrs)

In [15]:
l1.attrs


Out[15]:
Dict{Any,Any} with 2 entries:
  :lambda  => [-0.5]
  :redcost => [0.0, 0.5]

In [24]:
# Random vectors on a unit sphere
using Random
Random.seed!(423)
n=100
m=40
k=15
A=svd(rand(m,n)).Vt
using SparseArrays
# Compute a random vector
x=sprand(n,k/n)


Out[24]:
100-element SparseVector{Float64,Int64} with 8 stored entries:
  [6  ]  =  0.987869
  [32 ]  =  0.258024
  [34 ]  =  0.0995397
  [45 ]  =  0.726528
  [82 ]  =  0.881967
  [94 ]  =  0.912056
  [96 ]  =  0.262032
  [99 ]  =  0.438109

In [25]:
for i=1:size(A,2)
    normalize!(view(A,:,i))
end

In [26]:
diag(A'*A)


Out[26]:
100-element Array{Float64,1}:
 0.9999999999999997
 1.0               
 0.9999999999999999
 0.9999999999999999
 1.0000000000000002
 0.9999999999999998
 1.0000000000000002
 0.9999999999999999
 0.9999999999999997
 0.9999999999999999
 1.0000000000000002
 0.9999999999999999
 1.0               
 ⋮                 
 1.0               
 1.0000000000000002
 0.9999999999999998
 0.9999999999999998
 1.0               
 0.9999999999999997
 0.9999999999999997
 1.0               
 1.0               
 0.9999999999999999
 1.0000000000000002
 0.9999999999999998

In [27]:
# Check incoherence
μ=maximum(abs,A'*A-I)


Out[27]:
0.45798425776997853

In [28]:
# Sampling
b=A*x


Out[28]:
40-element Array{Float64,1}:
 -0.6988888504601383  
 -0.08147837669200228 
  0.3568030986020527  
 -0.34625433300587094 
 -0.1448801641969616  
 -0.6075066235299922  
 -0.16037373237614694 
  0.15352570039240182 
  0.01104006612321443 
  0.0737521216220456  
  0.157745364773333   
  0.04132607550468977 
 -0.29615405941577283 
  ⋮                   
 -0.25280908446467687 
 -0.1782796510687751  
  0.14148956337458038 
 -0.2183757381131815  
  0.30644504289693686 
 -0.0422721173957026  
  0.5371261971891701  
  0.1040690367725888  
  0.37254618174608456 
  0.12925786590647612 
 -0.019781124463033234
  0.4428959738306873  

In [29]:
# Recovery
c=ones(n)
l1=linprog(c,A,'=',b,0,Inf,ClpSolver())


Out[29]:
MathProgBase.HighLevelInterface.LinprogSolution(:Optimal, 4.566125127059287, [0.0, 0.0, 0.0, 0.0, 0.0, 0.987869, 0.0, 0.0, 0.0, 0.0  …  0.0, 0.0, 0.0, 0.912056, 0.0, 0.262032, 0.0, 0.0, 0.438109, 0.0], Dict{Any,Any}(:lambda=>[0.0, 0.0, -1.30252, -0.224437, -0.701215, -0.487042, 0.0, 0.0, 0.299285, 0.658006  …  0.0, 0.0, 0.297994, 0.0, 1.01296, 0.0, 1.41207, 0.0815033, 0.0, 0.0],:redcost=>[0.535209, 1.69471, 0.741501, 0.898531, 1.24115, 0.0, 2.42169, 0.969888, 1.02359, 0.0  …  0.0, 2.00119, 1.88817, 0.0, 0.47965, 0.0, 0.666016, 1.38521, 0.0, 1.72498]))

In [30]:
# Check
scatter(x)


Out[30]:
0 25 50 75 100 0.00 0.25 0.50 0.75 1.00 y1

In [31]:
scatter!(l1.sol)


Out[31]:
0 25 50 75 100 0.00 0.25 0.50 0.75 1.00 y1 y2

In [33]:
# for n=50:50:500, m=20:10:200, k=10:10:100
# m should not be too small
n=200
m=50
k=10
A=svd(rand(m,n)).Vt
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=sprand(n,k/n)
# Sampling
b=A*x
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
scatter([x l1.sol])


Out[33]:
0 50 100 150 200 0.00 0.25 0.50 0.75 1.00 y1 y2

In [39]:
# Normal distribution
# for n=50:50:500, m=20:10:200, k=10:10:100
n=200
m=50
k=20
A=rand(Normal(0,1/m),m,n)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=sprand(n,k/n)
# Sampling
b=A*x
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
# l1=linprog(ones(n),A,'=',b,0.0,Inf,GurobiSolver())
scatter([x l1.sol])


Out[39]:
0 50 100 150 200 0.00 0.25 0.50 0.75 1.00 y1 y2

In [47]:
# Symmetric Bernoulli distribution
# for n=50:50:500, m=20:10:200, k=10:10:100
n=200
m=50
k=20
# The matrix of (-1,1)s
A=2*(rand(Bernoulli(0.5),m,n).-0.5)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=sprand(n,k/n)
# Sampling
b=A*x
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
scatter([x l1.sol])


Out[47]:
0 50 100 150 200 0.00 0.25 0.50 0.75 1.00 y1 y2

In [48]:
nnz(x)


Out[48]:
15

In [49]:
A


Out[49]:
50×200 Array{Float64,2}:
  0.141421   0.141421  -0.141421  …   0.141421  -0.141421  -0.141421
 -0.141421   0.141421  -0.141421     -0.141421   0.141421  -0.141421
 -0.141421   0.141421  -0.141421      0.141421   0.141421   0.141421
  0.141421  -0.141421  -0.141421     -0.141421  -0.141421  -0.141421
  0.141421  -0.141421   0.141421     -0.141421   0.141421  -0.141421
 -0.141421  -0.141421  -0.141421  …  -0.141421  -0.141421   0.141421
  0.141421  -0.141421  -0.141421      0.141421  -0.141421   0.141421
 -0.141421  -0.141421   0.141421     -0.141421   0.141421  -0.141421
  0.141421  -0.141421   0.141421     -0.141421  -0.141421  -0.141421
 -0.141421   0.141421  -0.141421      0.141421   0.141421   0.141421
  0.141421  -0.141421   0.141421  …   0.141421   0.141421  -0.141421
  0.141421   0.141421   0.141421      0.141421  -0.141421  -0.141421
 -0.141421  -0.141421  -0.141421      0.141421  -0.141421   0.141421
  ⋮                               ⋱                                 
  0.141421   0.141421  -0.141421     -0.141421   0.141421  -0.141421
  0.141421   0.141421   0.141421     -0.141421  -0.141421   0.141421
 -0.141421   0.141421   0.141421  …   0.141421   0.141421   0.141421
 -0.141421  -0.141421  -0.141421      0.141421   0.141421  -0.141421
 -0.141421   0.141421   0.141421      0.141421   0.141421  -0.141421
 -0.141421  -0.141421  -0.141421      0.141421   0.141421  -0.141421
  0.141421  -0.141421  -0.141421      0.141421  -0.141421   0.141421
  0.141421   0.141421  -0.141421  …   0.141421   0.141421  -0.141421
 -0.141421   0.141421   0.141421      0.141421  -0.141421   0.141421
  0.141421  -0.141421   0.141421     -0.141421  -0.141421   0.141421
 -0.141421  -0.141421   0.141421     -0.141421  -0.141421   0.141421
  0.141421  -0.141421  -0.141421     -0.141421  -0.141421   0.141421

For Fourier transformation and Fourier matrix we use package FFTW.jl.


In [50]:
using FFTW

In [51]:
varinfo(FFTW)


Out[51]:
name size summary
AbstractFFTs 156.271 KiB Module
FFTW 172.700 KiB Module
bfft 0 bytes typeof(bfft)
bfft! 0 bytes typeof(bfft!)
brfft 0 bytes typeof(brfft)
dct 0 bytes typeof(dct)
dct! 0 bytes typeof(dct!)
fft 0 bytes typeof(fft)
fft! 0 bytes typeof(fft!)
fftshift 0 bytes typeof(fftshift)
idct 0 bytes typeof(idct)
idct! 0 bytes typeof(idct!)
ifft 0 bytes typeof(ifft)
ifft! 0 bytes typeof(ifft!)
ifftshift 0 bytes typeof(ifftshift)
irfft 0 bytes typeof(irfft)
plan_bfft 0 bytes typeof(plan_bfft)
plan_bfft! 0 bytes typeof(plan_bfft!)
plan_brfft 0 bytes typeof(plan_brfft)
plan_dct 0 bytes typeof(plan_dct)
plan_dct! 0 bytes typeof(plan_dct!)
plan_fft 0 bytes typeof(plan_fft)
plan_fft! 0 bytes typeof(plan_fft!)
plan_idct 0 bytes typeof(plan_idct)
plan_idct! 0 bytes typeof(plan_idct!)
plan_ifft 0 bytes typeof(plan_ifft)
plan_ifft! 0 bytes typeof(plan_ifft!)
plan_irfft 0 bytes typeof(plan_irfft)
plan_rfft 0 bytes typeof(plan_rfft)
rfft 0 bytes typeof(rfft)

In [53]:
# Fourier matrix
# for n=50:50:500, m=20:10:200, k=10:10:100
n=200
m=50
k=15
# Elegant way of computing the Fourier matrix
F=fft(Matrix(I,n,n),1)
# Select m/2 random rows
ind=Random.randperm(n)[1:round(Int,m/2)]
Fm=F[ind,:]
# We need to work with real matrices due to linprog()
A=[real(Fm); imag(Fm)]
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=sprand(n,k/n)
# Sampling
b=A*x
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
scatter([x l1.sol])


Out[53]:
0 50 100 150 200 0.0 0.2 0.4 0.6 0.8 y1 y2

Signal recovery from noisy observations

In the presence of noise in observation, we want to recover a vector $x$ from $b=Ax + z$, where $z$ is a stochastic or deterministic unknown error term.

Definition

The hard thresholding operator, $H_k(x)$, sets all but the $k$ entries of $x$ with largest magnitude to zero.

Facts

  1. The problem can be formulated as $l_1$ minimization problem $$ \textrm{minimize}\ \|x\|_1 \quad \textrm{subject to} \quad \|Ax-b\|_2^2\leq\epsilon, $$ where $\epsilon$ bounds the amount of noise in the data.

  2. Assume that $A$ satisfies RIP of order $2k$ with $\delta_{2k}< \sqrt{2}-1$. Then the solution $x^{\star}$ of the above problem satisfies $$ \|x^{\star}-x\|_2 \leq C_0 \displaystyle \frac{1}{\sqrt{k}}\|x-H_k(x)\|_1 +C_1\epsilon, $$ where $x$ is the original signal.

  3. The $l_1$ problem is a convex programming problem and can be efficiently solved. The solution methods are beyond the scope of this course.

  4. If $k$ is known in advance, $A$ satisfies RIP with $\delta_{3k}<1/15$, and $\|A\|_2<1$, the $k$-sparse aproximation of $x$ can be computed by the Iterative Hard Thresholding algorithm

    1. Initialization: $x=0$.
    2. Iteration: repeat until convergence $x=H_k(x+A^T(b-Ax))$.

Example

We construct the $k$ sparse $x$, form $b$, add noise, and recover it with the algorithm from Fact 4. The conditions on $A$ are rather restrictive, which means that $k$ must be rather small compared to $n$ and $m$ must be rather large. For convergence, we limit the number of iterations to $50m$.


In [55]:
n=300
# k is small compared to n
k=8
x=10*sprand(n,k/n)
# Reset k
k=nnz(x)
# Define m, rather large
m=5*round(Int,k*log(n/k))
# Sampling matrix - normal distribution
A=rand(Normal(0,1/m),m,n)
# A=A/(norm(A)+1)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Form b
b=A*x
# Add noise
noise=rand(m)*1e-5
b.+=noise


Out[55]:
130-element Array{Float64,1}:
 -1.2498429896331789 
 -0.33857583208410913
 -0.31469898189068   
  1.7839980210255142 
  1.2196669938102997 
 -0.7223628612402943 
  0.8397966069844514 
 -0.8772598349949995 
 -0.4872900545697539 
  1.2021440123800349 
 -1.5016765662614477 
 -1.197612479418941  
 -3.213719358127324  
  ⋮                  
  0.5768047545691513 
  1.3437636963128134 
 -0.1950819403203296 
  1.3429289850524078 
  1.418104739585158  
 -0.2764381537743857 
 -0.9791779868521472 
  1.351197203999753  
  3.0645442746172136 
 -3.001348813596022  
  0.03894375982786044
  0.5373685967312785 

In [56]:
A


Out[56]:
130×300 Array{Float64,2}:
 -0.0436745    0.00409386   0.0126637    …   0.0583793   0.184557  
  0.0528953    0.0551988    0.131375        -0.0802555   0.0122403 
  0.0549541   -0.0834871   -0.0350921       -0.241917    0.0635025 
  0.00631469  -0.0568489    0.0998013       -0.0358849   0.0216909 
 -0.0489335   -0.0232951    0.00361991       0.0398303  -0.19568   
 -0.0259894   -0.133274    -0.0233726    …   0.0420808  -0.107026  
  0.0955095    0.0267904   -0.147714         0.0314325   0.0470684 
  0.0927276    0.0345372    0.000629955      0.114523   -0.00742696
  0.0356642   -0.109064     0.0445912        0.0848761  -0.0215433 
 -0.1691       0.0593498    0.10734         -0.04997     0.0220093 
  0.129096    -0.095027     0.0435909    …  -0.0895193  -0.0488505 
  0.00606185   0.180178     0.0425421       -0.0701354   0.0163611 
  0.0423592    0.023426     0.000114996      0.0075225  -0.0565252 
  ⋮                                      ⋱                         
  0.101679     0.0857271   -0.0959576       -0.074284   -0.123671  
 -0.036056     0.031592    -0.0259102        0.0761318   0.0685204 
 -0.0934536    0.116019    -0.0476936    …  -0.141339    0.0235222 
 -0.192049     0.168871     0.0964695        0.0324526   0.0923176 
 -0.0200321   -0.013165     0.0387964        0.0608146   0.0991705 
 -0.02261      0.0110248   -0.176282         0.0236627   0.0177896 
 -0.124852    -0.113172     0.0321043        0.0203581  -0.0338008 
 -0.0236797   -0.0676412   -0.103207     …  -0.191595    0.0040042 
 -0.11148     -0.130939     0.167089        -0.0134827   0.0583201 
  0.0268377    0.0169865    0.00485887      -0.0276572   0.0323936 
 -0.0665799   -0.0488036   -0.132523        -0.104241    0.0161759 
  0.00389745   0.210191    -0.0898638       -0.069629    0.0999347 

In [57]:
norm(A),k,m


Out[57]:
(17.320508075688775, 7, 130)

In [58]:
x


Out[58]:
300-element SparseVector{Float64,Int64} with 7 stored entries:
  [56 ]  =  5.88455
  [73 ]  =  2.53375
  [76 ]  =  8.73548
  [190]  =  7.67571
  [235]  =  1.15093
  [243]  =  7.23827
  [261]  =  3.82113

In [59]:
# Iterative Hard Thresholding 
function H(x::Vector,k::Int)
    y=deepcopy(x)
    ind=sortperm(abs.(y),rev=true)
    y[ind[k+1:end]].=0
    y
end


Out[59]:
H (generic function with 1 method)

In [60]:
function IHT(A::Matrix, b::Vector,k::Int)
    # Tolerance
    τ=1e-12
    x=zeros(size(A,2))
    for i=1:50*m
        x=H(x+A'*(b-A*x),k)
    end
    x
end


Out[60]:
IHT (generic function with 1 method)

In [61]:
y=IHT(A,b,k)
norm(A*x-b)/norm(b)


Out[61]:
4.347879147224011e-6

In [62]:
println(x)


  [56 ]  =  5.88455
  [73 ]  =  2.53375
  [76 ]  =  8.73548
  [190]  =  7.67571
  [235]  =  1.15093
  [243]  =  7.23827
  [261]  =  3.82113

In [63]:
println(sparse(y))


  [56 ]  =  5.88455
  [73 ]  =  2.53376
  [76 ]  =  8.73547
  [190]  =  7.67571
  [235]  =  1.15093
  [243]  =  7.23827
  [261]  =  3.82113

In [64]:
scatter([x y])


Out[64]:
0 100 200 300 0 2 4 6 8 y1 y2

Let us try linear programing in the case of noisy observations.


In [65]:
# Try with noise
# Normal distribution
# for n=50:50:500, m=20:10:200, k=10:10:100
n=300
# k is small compared to n
k=8
m=5*round(Int,k*log(n/k))
A=rand(Normal(0,1/m),m,n)
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Compute a random vector
x=1*sprand(n,k/n)
# Sampling with noise
b=A*x
# Add noise
noise=(rand(m).-0.5)*1e-3
b.+=noise
# Recovery
l1=linprog(ones(n),A,'=',b,0.0,Inf,ClpSolver())
# method=:Simplex or method=:InteriorPoint
#l1=linprog(ones(n),A,'=',b,0.0,Inf,
#     GLPKSolverLP(presolve=true,method=:Simplex))


Out[65]:
MathProgBase.HighLevelInterface.LinprogSolution(:Optimal, 4.248514607305773, [0.936316, 0.0, 0.00122194, 0.000507972, 0.0, 0.000772644, 0.0, 0.000186965, 0.0, 0.000276423  …  0.000272407, 0.0, 0.000134572, 0.0, 0.0, 0.0, 0.000247492, 0.000655143, 0.000426973, 0.0], Dict{Any,Any}(:lambda=>[6.83794, -5.022, -1.78637, 2.4143, -1.69932, -10.878, -0.861418, -13.5511, 2.88177, -2.83574  …  1.19249, -2.93997, -7.66452, -13.9279, 8.71083, -3.26088, -3.49818, 6.1862, -7.9812, 11.2863],:redcost=>[0.0, 6.47154, 0.0, 0.0, 6.49782, 0.0, 7.44669, 0.0, 3.65777, 0.0  …  0.0, 2.31924, 0.0, 5.54839, 11.2595, 2.26731, 0.0, 0.0, 0.0, 1.86315]))

In [66]:
scatter([x l1.sol])


Out[66]:
0 100 200 300 0.0 0.2 0.4 0.6 0.8 y1 y2

In [67]:
x


Out[67]:
300-element SparseVector{Float64,Int64} with 8 stored entries:
  [1  ]  =  0.936915
  [104]  =  0.888013
  [106]  =  0.433179
  [131]  =  0.0950182
  [179]  =  0.946057
  [185]  =  0.0185474
  [214]  =  0.17257
  [263]  =  0.665937

In [68]:
sparse(l1.sol)


Out[68]:
300-element SparseVector{Float64,Int64} with 145 stored entries:
  [1  ]  =  0.936316
  [3  ]  =  0.00122194
  [4  ]  =  0.000507972
  [6  ]  =  0.000772644
  [8  ]  =  0.000186965
  [10 ]  =  0.000276423
  [12 ]  =  0.000180135
  [15 ]  =  7.52221e-6
  [18 ]  =  0.000573377
  [19 ]  =  0.000155018
         ⋮
  [282]  =  0.00045965
  [284]  =  0.000262892
  [285]  =  0.000556371
  [287]  =  0.000807306
  [288]  =  0.00117061
  [290]  =  0.00020255
  [291]  =  0.000272407
  [293]  =  0.000134572
  [297]  =  0.000247492
  [298]  =  0.000655143
  [299]  =  0.000426973

Sensing images

Wavelet transformation of an image is essentially sparse, since only small number of cofficients is significant. This fact can be used for compression.

Wavelet transforms are implemented the package Wavelets.jl.

Example - Lena

The tif version of the image has 65_798 bytes, the png version has 58_837 bytes, and the jpeg version has 26_214 bytes.


In [69]:
# import Pkg; Pkg.add("Wavelets")
# import Pkg; Pkg.add("TestImages")

In [71]:
using Wavelets
using Images
using TestImages

In [72]:
varinfo(TestImages)


Out[72]:
name size summary
TestImages 8.344 KiB Module
testimage 0 bytes typeof(testimage)

In [73]:
println(TestImages.remotefiles)


["autumn_leaves.png", "blobs.gif", "cameraman.tif", "earth_apollo17.jpg", "fabio_color_256.png", "fabio_color_512.png", "fabio_gray_256.png", "fabio_gray_512.png", "hela-cells.tif", "house.tif", "jetplane.tif", "lake_color.tif", "lake_gray.tif", "lena_color_256.tif", "lena_color_512.tif", "lena_gray_256.tif", "lena_gray_512.tif", "lena_gray_16bit.png", "livingroom.tif", "lighthouse.png", "mandril_color.tif", "mandril_gray.tif", "mandrill.tiff", "m51.tif", "moonsurface.tiff", "mountainstream.png", "mri-stack.tif", "multi-channel-time-series.ome.tif", "peppers_color.tif", "peppers_gray.tif", "pirate.tif", "toucan.png", "walkbridge.tif", "woman_blonde.tif", "woman_darkhair.tif"]

In [74]:
img=testimage("lena_gray_256.tif")


Out[74]:

In [75]:
typeof(img)


Out[75]:
Array{Gray{Normed{UInt8,8}},2}

In [76]:
size(img)


Out[76]:
(256, 256)

In [77]:
show(img[1,1])


Gray{N0f8}(0.635)

In [78]:
# The abpove image is too big, so we use smaller one
img=imresize(img,ratio=1/4)


Out[78]:

In [79]:
size(img)


Out[79]:
(64, 64)

In [80]:
# Convert the image to 32 or 64 bit floats
x=map(Float32,img)
" Number of matrix elements = ",prod(size(x)), " Bytes = ",sizeof(x)


Out[80]:
(" Number of matrix elements = ", 4096, " Bytes = ", 16384)

In [81]:
# Compute the wavelet transform of x
# wlet=WT.haar
wlet=WT.haar
xₜ=dwt(x,wavelet(wlet),6)
# xₜ=dct(x)


Out[81]:
64×64 Array{Float32,2}:
 31.1186       3.3326       0.439092    …   0.00392155  -0.0294118 
 -1.67758      1.78272     -0.792034       -0.164706     0.0156863 
 -0.772917     0.908824    -0.439338        0.0392157    0.00392156
 -0.564827    -0.0634813    0.122426       -0.00588235   0.00196078
 -0.498039     0.227451     0.539951       -0.00588233   0.00196079
 -0.0914221   -1.20392     -0.938725    …   0.0705882    0.158824  
 -0.308334    -0.141912    -0.34951         0.152941     0.0392157 
  0.0210783    0.15147      0.58799         0.0352941   -0.00588235
 -0.0259805   -0.0308825   -0.0421569      -0.0156863   -0.00784313
 -0.186765    -0.00539207   0.029902       -0.00980393  -0.00588235
  0.0460787   -0.0401961    0.209314    …  -0.00196078  -0.0137255 
 -0.0230391    0.072549    -0.671569       -0.0019608   -0.00392155
 -0.151961    -0.133333     0.0607842      -0.00980393  -0.0137255 
  ⋮                                     ⋱                          
 -0.00588235   0.0607843    0.00196075      0.00196084   0.0       
 -0.0333333   -0.0137255    0.00588238      0.00784311   0.00588235
 -0.0254902   -0.0196078    0.0            -0.0156863    0.0039216 
 -0.00980392  -0.00980389   0.00784314  …  -0.0176471   -0.0156863 
  0.0627451   -0.0627451   -0.0156863      -0.168627     0.0921569 
  0.0745098    0.0627451    0.00980395     -0.0627451   -0.0372549 
 -0.054902     0.0176471    0.0            -0.0411765   -0.00980393
 -0.0117647    0.0176471   -0.0137255       0.0294117   -0.0176471 
 -0.0372549    0.0333333   -0.0156863   …  -0.0509804   -0.0117647 
  0.0980392    0.0490196   -0.00196075      0.0431373    0.00392156
 -0.0960784    0.00196081  -0.00588232     -0.054902     0.027451  
 -0.0392157    0.0862746   -0.00588232     -0.0156863    0.0764706 

In [82]:
colorview(Gray,xₜ)


Out[82]:

We now set all but the 10% absolutely largest coefficients to zero and reconstruct the image. The images are very similar, which illustrates that the wavelet transform of an image is essentially sparse.


In [85]:
ind=sortperm(abs.(vec(xₜ)),rev=true)
# 0.1 = 10%, try also 0.05 = 5%
τ=0.3
k=round(Int,τ*length(ind))
xsparse=copy(xₜ)
xsparse[ind[k+1:end]].=0
# Inverse wavelet transform of the sparse data
imgsparse=idwt(xsparse, wavelet(wlet),6)
# imgsparse=idct(xsparse)
# Original and sparse image
display(img)
display(colorview(Gray,imgsparse))
" Number of non-zero elements = ",nnz(sparse(xsparse))


Out[85]:
(" Number of non-zero elements = ", 1229)

In [86]:
typeof(xsparse)


Out[86]:
Array{Float32,2}

In [87]:
nnz(sparse(xsparse))


Out[87]:
1229

In [88]:
prod(length(xsparse))


Out[88]:
4096

There are $k=6554 (1638) $ nonzero coefficients in a sparse wavelet representation.

Is there the sensing matrix which can be used to sample and recover xsparse?

Actual algorithms are elaborate. For more details see J. Romberg, Imaging via Compressive Sampling and Duarte et al.,Single-Pixel Imaging via Compressive Sampling.


In [89]:
function Hadamard(n::Int)
    # Hadamard matrix 2^n
    H=Array{Int}(undef,1,1)
    H[1,1]=1
    for i=1:n
        H1=[H H;H -H]
        H=H1
    end
    H
end


Out[89]:
Hadamard (generic function with 1 method)

In [90]:
n=prod(size(x))
m=div(n,3)
m=1600

# A=rand(Normal(0,1/m),m,n)
A=2*(rand(Bernoulli(0.5),m,n).-0.5)
#=
Had=map(Float32,Hadamard(12))
ind=Random.randperm(n)[1:round(Int,m)]
A=Had[ind,:]
=#
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Sampling
b=A*xsparse[:]
# Recovery
# This is too slow right now
# l1=linprog(ones(n),A,'=',b,0,Inf,ClpSolver())


Out[90]:
1600-element Array{Float64,1}:
 -0.7064030500128863 
  0.904902056045829  
  0.9872487088665345 
  0.867083272524178  
  0.7233640143647799 
 -0.7436827013269051 
 -1.3698529737070213 
  0.9806801686063417 
  0.5523344697430728 
 -0.8088047703728082 
 -0.952873584814372  
 -0.5885722288861878 
  1.0730941450223328 
  ⋮                  
  0.4398162418976428 
 -1.3839213145896796 
 -0.3034191614016892 
 -0.6439520912244926 
 -0.8026469932869084 
 -0.5126163756474846 
  0.838970536924899  
 -1.0150919744744897 
 -0.36744507979601587
 -0.9040807081386436 
  1.1085353201255195 
 -0.9815870666876424 

In [91]:
A


Out[91]:
1600×4096 Array{Float64,2}:
 -0.025  -0.025  -0.025  -0.025  -0.025  …   0.025  -0.025  -0.025  -0.025
  0.025   0.025  -0.025  -0.025  -0.025     -0.025   0.025   0.025  -0.025
  0.025  -0.025   0.025  -0.025  -0.025     -0.025   0.025  -0.025  -0.025
  0.025  -0.025   0.025  -0.025  -0.025      0.025   0.025  -0.025  -0.025
  0.025  -0.025  -0.025  -0.025  -0.025     -0.025   0.025   0.025  -0.025
 -0.025   0.025   0.025  -0.025  -0.025  …   0.025  -0.025  -0.025   0.025
 -0.025   0.025   0.025   0.025  -0.025      0.025  -0.025   0.025  -0.025
  0.025   0.025  -0.025  -0.025  -0.025      0.025  -0.025  -0.025  -0.025
  0.025   0.025  -0.025   0.025  -0.025      0.025   0.025  -0.025  -0.025
 -0.025  -0.025   0.025   0.025   0.025     -0.025  -0.025  -0.025  -0.025
 -0.025  -0.025   0.025  -0.025   0.025  …   0.025   0.025   0.025  -0.025
 -0.025   0.025  -0.025  -0.025  -0.025     -0.025   0.025   0.025   0.025
  0.025   0.025   0.025  -0.025   0.025     -0.025  -0.025   0.025   0.025
  ⋮                                      ⋱                           ⋮    
  0.025  -0.025  -0.025  -0.025  -0.025     -0.025  -0.025   0.025   0.025
 -0.025  -0.025   0.025  -0.025  -0.025      0.025  -0.025  -0.025  -0.025
 -0.025   0.025   0.025  -0.025  -0.025  …   0.025  -0.025  -0.025   0.025
 -0.025  -0.025  -0.025   0.025  -0.025     -0.025  -0.025  -0.025  -0.025
 -0.025   0.025   0.025   0.025  -0.025     -0.025  -0.025   0.025  -0.025
 -0.025   0.025   0.025   0.025   0.025      0.025   0.025   0.025  -0.025
  0.025  -0.025   0.025   0.025  -0.025     -0.025  -0.025   0.025   0.025
 -0.025   0.025  -0.025  -0.025  -0.025  …  -0.025   0.025  -0.025   0.025
 -0.025   0.025  -0.025  -0.025   0.025      0.025   0.025   0.025   0.025
 -0.025  -0.025   0.025   0.025   0.025      0.025  -0.025   0.025  -0.025
  0.025   0.025  -0.025  -0.025   0.025     -0.025   0.025   0.025   0.025
 -0.025  -0.025  -0.025   0.025   0.025     -0.025  -0.025   0.025  -0.025

In [93]:
# Mutual incoherence
maximum(abs.(A'*A-I))


Out[93]:
0.12875

In [94]:
length(b),sizeof(b)


Out[94]:
(1600, 12800)

In [95]:
sizeof(x)


Out[95]:
16384

In [96]:
# Neither of this is currently working well
l1=linprog(ones(Float32,n),A,'=',b,0,Inf,GLPKSolverLP())
#=
l1=linprog(ones(Float32,n),A,'=',b,0,Inf,ClpSolver(PrimalTolerance=1e-4,
        DualTolerance=1e-4,MaximumSeconds=100,PresolveType=1))
=#


Out[96]:
MathProgBase.HighLevelInterface.LinprogSolution(:Optimal, 595.3427422291294, [31.6909, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.785135, 0.00263128  …  0.0, 0.0, 0.0103401, 0.0, 0.396899, 0.0, 0.367352, 0.00256076, 0.0, 0.173725], Dict{Any,Any}(:lambda=>[4.83938, 0.126514, 5.76819, 3.24143, -1.54157, -4.36764, -10.5494, 3.39115, -2.79138, 2.13732  …  5.80744, 2.60732, -0.501603, -4.59588, 0.384649, 1.48555, 1.81635, -5.95214, -0.397772, -6.23046],:redcost=>[0.0, 14.3082, 3.59115, 5.00066, 6.41533, 1.73009, 9.11949, 4.97245, 0.0, 0.0  …  2.05637, 0.491393, 0.0, 2.73643, 0.0, 1.03494, 0.0, 0.0, 1.14766, 0.0]))

In [447]:
# l1=linprog(ones(Float32,n),A,'=',b,0,Inf,GurobiSolver())

In [97]:
l1.sol


Out[97]:
4096-element Array{Float64,1}:
 31.69094753287405     
  0.0                  
  0.0                  
  0.0                  
  0.0                  
  0.0                  
  0.0                  
  0.0                  
  0.7851348335809315   
  0.0026312756527248063
  0.8576071902122012   
  0.31469109120598304  
  0.442757244591529    
  ⋮                    
  0.0                  
  0.0                  
  0.0                  
  0.0                  
  0.010340104194978918 
  0.0                  
  0.3968991273506274   
  0.0                  
  0.3673515914428176   
  0.0025607634921479346
  0.0                  
  0.1737248354359752   

In [98]:
nnz(sparse(l1.sol))


Out[98]:
1600

In [99]:
length(l1.sol)


Out[99]:
4096

In [100]:
# Form the matrix
xrecover=reshape(l1.sol,64,64)


Out[100]:
64×64 Array{Float64,2}:
 31.6909      3.69789    0.0       …  0.667691   0.0        0.0       
  0.0         1.36368    0.0          0.0        0.0        0.0       
  0.0         1.0751     0.0          0.0        0.0        0.0       
  0.0         0.0        0.0          0.0        0.15534    0.151536  
  0.0         0.302137   0.648983     0.0212731  0.0        0.470413  
  0.0         0.0        0.0       …  0.661892   0.0        0.402279  
  0.0         0.0        0.0          0.306247   0.0        0.0       
  0.0         0.0        0.796937     0.0        0.413857   0.195032  
  0.785135    0.0        0.0          0.652427   0.0        0.431896  
  0.00263128  0.0        0.265782     0.0        0.0        0.0       
  0.857607    0.0        0.472237  …  0.0        0.0        0.0       
  0.314691    0.0        0.0          0.272374   0.0        0.0       
  0.442757    0.0        0.150573     0.0        0.0951084  0.0       
  ⋮                                ⋱                                  
  0.0         0.0        0.180394     0.0        0.0        0.0       
  0.843868    0.100621   0.0          0.0        0.271773   0.0       
  0.0         0.0        0.0          0.411982   0.0        0.0       
  0.0         0.408319   0.344253  …  0.0        0.0        0.0       
  0.51054     0.648947   0.0          0.0        0.0        0.0103401 
  0.0         0.0228667  0.318774     0.0        0.414958   0.0       
  0.0         0.0        0.0          0.210507   0.0        0.396899  
  0.0         0.0        0.0          0.242453   0.0        0.0       
  0.0         0.131554   0.0       …  0.0        0.397238   0.367352  
  0.0         0.0        0.0          0.0        0.221291   0.00256076
  0.0         0.352581   0.0          0.0        0.45588    0.0       
  0.918012    0.0        0.0          0.558465   0.0        0.173725  

In [101]:
xsparse


Out[101]:
64×64 Array{Float32,2}:
 31.1186      3.3326      0.439092  …   0.0        0.0        0.0      
 -1.67758     1.78272    -0.792034      0.0       -0.164706   0.0      
 -0.772917    0.908824   -0.439338     -0.164706   0.0        0.0      
 -0.564827    0.0         0.122426      0.0        0.0        0.0      
 -0.498039    0.227451    0.539951      0.0        0.0        0.0      
 -0.0914221  -1.20392    -0.938725  …   0.0        0.0705882  0.158824 
 -0.308334   -0.141912   -0.34951       0.0        0.152941   0.0      
  0.0         0.15147     0.58799       0.298039   0.0        0.0      
  0.0         0.0         0.0           0.0        0.0        0.0      
 -0.186765    0.0         0.0           0.0        0.0        0.0      
  0.0         0.0         0.209314  …   0.0        0.0        0.0      
  0.0         0.072549   -0.671569      0.0        0.0        0.0      
 -0.151961   -0.133333    0.0           0.0        0.0        0.0      
  ⋮                                 ⋱                                  
  0.0         0.0         0.0           0.0        0.0        0.0      
  0.0         0.0         0.0           0.0        0.0        0.0      
  0.0         0.0         0.0           0.0        0.0        0.0      
  0.0         0.0         0.0       …   0.0        0.0        0.0      
  0.0         0.0         0.0           0.0       -0.168627   0.0921569
  0.0745098   0.0         0.0          -0.184314   0.0        0.0      
  0.0         0.0         0.0           0.145098   0.0        0.0      
  0.0         0.0         0.0           0.0        0.0        0.0      
  0.0         0.0         0.0       …   0.0        0.0        0.0      
  0.0980392   0.0         0.0           0.0        0.0        0.0      
 -0.0960784   0.0         0.0           0.0        0.0        0.0      
  0.0         0.0862746   0.0           0.0        0.0        0.0764706

In [102]:
imgrecover=idwt(xrecover, wavelet(wlet),6)
# imgrecover=idct(xrecover)
# Original and recovered image
display(img)
display(colorview(Gray,imgrecover))



In [114]:
img=load("./files/RDuarte.png")


Out[114]:

In [116]:
img=imresize(img,ratio=1/4)


Out[116]:

In [117]:
size(img)


Out[117]:
(47, 47)

In [120]:
x=map(Gray,img)
x=map(Float32,x)
wlet=WT.haar
xₜ=dwt(x,wavelet(wlet))


Out[120]:
47×47 Array{Float32,2}:
 0.109804  0.113725  0.113725  0.101961   …  0.113725  0.117647  0.117647
 0.109804  0.113725  0.113725  0.113725      0.109804  0.121569  0.121569
 0.109804  0.113725  0.113725  0.117647      0.109804  0.101961  0.109804
 0.109804  0.109804  0.113725  0.109804      0.121569  0.105882  0.109804
 0.105882  0.109804  0.109804  0.105882      0.12549   0.113725  0.113725
 0.105882  0.109804  0.113725  0.113725   …  0.117647  0.113725  0.113725
 0.101961  0.105882  0.109804  0.109804      0.113725  0.117647  0.113725
 0.101961  0.105882  0.109804  0.105882      0.117647  0.121569  0.117647
 0.101961  0.105882  0.113725  0.109804      0.113725  0.121569  0.117647
 0.105882  0.113725  0.117647  0.117647      0.113725  0.117647  0.117647
 0.101961  0.113725  0.117647  0.117647   …  0.113725  0.113725  0.109804
 0.105882  0.109804  0.109804  0.109804      0.121569  0.113725  0.105882
 0.109804  0.105882  0.105882  0.105882      0.121569  0.117647  0.113725
 ⋮                                        ⋱            ⋮                 
 0.109804  0.113725  0.113725  0.109804   …  0.117647  0.12549   0.133333
 0.113725  0.117647  0.121569  0.117647      0.129412  0.129412  0.133333
 0.113725  0.117647  0.121569  0.105882      0.137255  0.137255  0.129412
 0.105882  0.109804  0.109804  0.0980392     0.133333  0.133333  0.129412
 0.105882  0.105882  0.109804  0.109804      0.12549   0.121569  0.121569
 0.109804  0.109804  0.109804  0.109804   …  0.133333  0.121569  0.129412
 0.113725  0.113725  0.113725  0.113725      0.137255  0.129412  0.129412
 0.105882  0.105882  0.109804  0.113725      0.152941  0.133333  0.129412
 0.101961  0.109804  0.113725  0.113725      0.14902   0.133333  0.133333
 0.101961  0.109804  0.109804  0.109804      0.129412  0.129412  0.133333
 0.101961  0.101961  0.113725  0.105882   …  0.129412  0.133333  0.137255
 0.105882  0.109804  0.105882  0.0980392     0.129412  0.121569  0.129412

In [128]:
xₜ=dct(vec(x))


Out[128]:
2209-element Array{Float32,1}:
 13.479267     
 -0.0546058    
 -3.033405     
 -0.14657083   
 -4.7519894    
 -0.6970225    
  0.6372694    
  0.7438212    
  1.7811707    
  0.2627325    
  0.0536847    
 -0.1741147    
 -0.016997725  
  ⋮            
  0.00048883236
  0.004906947  
 -0.004230504  
 -0.008448344  
  0.0034296308 
  0.00724552   
 -0.0031771234 
 -0.005563686  
 -0.0031546038 
  0.005339343  
  0.0050128847 
  0.007134682  

In [131]:
ind=sortperm(abs.(vec(xₜ)),rev=true)
# 0.1 = 10%, try also 0.05 = 5%
τ=0.1
k=round(Int,τ*length(ind))
xsparse=copy(xₜ)
xsparse[ind[k+1:end]].=0
# Inverse wavelet transform of the sparse data
# imgsparse=idwt(xsparse, wavelet(wlet),6)
imgsparse=idct(xsparse)
# Original and sparse image
display(img)
display(colorview(Gray,reshape(imgsparse,47,47)))
" Number of non-zero elements = ",nnz(sparse(xsparse))


Out[131]:
(" Number of non-zero elements = ", 221)

In [134]:
n=prod(size(x))
m=div(n,3)
m=800

# A=rand(Normal(0,1/m),m,n)
A=2*(rand(Bernoulli(0.5),m,n).-0.5)
#=
Had=map(Float32,Hadamard(12))
ind=Random.randperm(n)[1:round(Int,m)]
A=Had[ind,:]
=#
for i=1:size(A,2)
    normalize!(view(A,:,i))
end
# Sampling
b=A*xsparse
# Recovery
# This is too slow right now
# l1=linprog(ones(n),A,'=',b,0,Inf,ClpSolver())


Out[134]:
800-element Array{Float64,1}:
 -0.9040903855042675 
  0.5647586544729006 
 -0.8767209074944167 
 -0.5635874025089876 
 -0.1628746850729531 
  0.6829890192037698 
  0.5785658092627436 
  0.22656913938783574
  0.2774377703285344 
 -0.4717912677364723 
 -0.390011517485539  
 -0.34023353318781074
 -0.20200130622462464
  ⋮                  
 -0.7800351513997712 
  0.6430123909480426 
 -0.5427714191067794 
 -0.34945310835752913
  0.3685440749874181 
 -0.900498824949672  
 -0.6403349423272524 
  0.44928682731885006
 -1.1665910522003344 
  1.1295883150585766 
  0.35962566539209084
  0.14588264941131687

In [135]:
l1=linprog(ones(Float32,n),A,'=',b,0,Inf,GLPKSolverLP())


Out[135]:
MathProgBase.HighLevelInterface.LinprogSolution(:Optimal, 372.4934305826865, [12.7095, 0.0, 0.0, 0.354853, 0.0, 0.294259, 0.0, 0.0, 1.52717, 0.597097  …  0.0, 0.358518, 0.0, 0.0, 0.255787, 0.0, 0.0, 0.0, 0.0342943, 0.0], Dict{Any,Any}(:lambda=>[-4.38399, 5.04003, -0.157449, 4.1363, 0.79626, 8.34326, 0.390895, -3.04999, 1.48162, 0.207618  …  0.116243, 2.97673, 3.1663, 0.00251091, -5.56668, -1.60859, -2.36718, 6.40644, -3.57622, -2.51442],:redcost=>[0.0, 2.6738, 16.5785, 0.0, 24.339, 0.0, 5.87941, 0.766521, 0.0, 0.0  …  1.95613, 0.0, 6.78174, 1.31256, 0.0, 4.63291, 4.34394, 4.07617, 0.0, 0.00551994]))

In [137]:
xrecover=reshape(l1.sol,47,47)
# imgrecover=idwt(xrecover, wavelet(wlet),6)
imgrecover=idct(xrecover)
# Original and recovered image
display(img)
display(colorview(Gray,imgrecover))



In [138]:
[xsparse l1.sol]


Out[138]:
2209×2 Array{Float64,2}:
 13.4793    12.7095   
  0.0        0.0      
 -3.03341    0.0      
 -0.146571   0.354853 
 -4.75199    0.0      
 -0.697022   0.294259 
  0.637269   0.0      
  0.743821   0.0      
  1.78117    1.52717  
  0.262733   0.597097 
  0.0        0.0      
 -0.174115   0.0      
  0.0        0.0      
  ⋮                   
  0.0        0.0      
  0.0        0.0      
  0.0        0.0      
  0.0        0.358518 
  0.0        0.0      
  0.0        0.0      
  0.0        0.255787 
  0.0        0.0      
  0.0        0.0      
  0.0        0.0      
  0.0        0.0342943
  0.0        0.0      

In [ ]: